/*
PARI/GP scripts defining generic formatted file I/O utilities
=======================================================================
File: EbIo.txt
Link: https://oeis.org/wiki/File:EbIo.txt
=======================================================================
Last updated 19 Nov 2016.
Dependencies: none

This file provides support for various types of formatted
output into files to be used elsewhere (e.g., as input to
Excell. Matlab, or as OEIS entries, etc).

It contains these functions:

> ExpFormat(z,d=0,head="",sep=" ",trail="")
> Real2ExpFormat(x,d=0) ... used internally by ExpFormat

> TabFuncArr(f,x,d=0,s=" ",file="",head="")
> TabFuncLinX(f,xini,xstep,xend,d=0,s=" ",file="",head="")

> Real2OEIS(file,cnst,dgts=1000)
> IntSeq2OEIS(file,vect,offset,dumpall=0)
*/


\\ ====================================================================
\\ Converts a number to the mantissa-exponent string format with
\\ a desired number of decimal digits (typically < default_precision)
\\ ====================================================================

ExpFormat(z,d=0,head="",trail="",sep=" ") =
/* --------------------------------------------------------------------
When z is of type t_REAL or t_INT, calls Real2ExpFormat(z,d), preceded
by the "head" string and followed by "trail".
When z is of type t_COMPLEX, dumps its real and imaginary parts,
separated by the sep string. Each part p is formatted using a call to
Real2ExpFormat(p,d). The whole is bracketed by the "head" ... "trail". 

Note: if sep is "+", it skips it for negative imaginary parts. This
is typically used in conjunction with "*i" as trail to outpout
complex numbers in the format real+imag*i, or real-imag*i

Note: For t_REAL (or t_INT) values, the argument sep is irrelevant.

Examples:
This decodes correctly the tabulator escape sequence:
   print(ExpFormat(exp(1+I),,,"\t"))

This adds "+" in front of positive imaginary parts:
ExpFormat(exp(1+I),,,"+","*i")
-------------------------------------------------------------------- */
{
  if(type(z)!="t_COMPLEX",
     return(Str(head,Real2ExpFormat(z,d),trail)));
  my(mysep=sep,p=imag(z));
  if((p<0)&(sep=="+"),mysep="");
  return(Str(head,Real2ExpFormat(real(z),d),
             mysep,Real2ExpFormat(p,d),trail)); 
}

Real2ExpFormat(x,d=0) =
/* --------------------------------------------------------------------
Returns a string composed of a decimal mantissa m such that
1.0<=m<10.0, having dprec digits after the decimal point,
and e##..# with ##..# being the decimal exponent.
The actual precision of x is tested and the number of
digits of the mantissa is not allowed to exceed it. When
dprec <=0 (the default), all significant digits are used.
When the number of output mantissa digits is smaller than
the precision of x, the last mantissa digit is rounded.
There is no blank space before the exponent "e" character,
making the outputmore suitable for interfacing with
third party programs (for example Excell, Origin, etc).
Note: For values very close to a power of 10, this function
sometimes returns strings like "10.0000e23", starting with
"10." instead of "1.", as in the desired "1.0000e24".
This is a minor problem, because the value is correct;
so far, I have not found how to circumvent it.
-------------------------------------------------------------------- */
{
  if((type(x)!="t_REAL")&&((type(x)!="t_INT")),
	error("Real2ExpFormat: illegal argument type"));
  my(m=abs(x)+0.0);
  my(e=0,prc=precision(m)-1);
  if((d>0)&&(d<prc),prc=d);
  if(m>0,
    while(m-1<0,e--;m*=10);
    while(m-10>=0,e++;m/=10);
    if(x>0,
    return(Strprintf(Str("%." ,prc,"fe%i"),m,e)),
    return(Strprintf(Str("-%.",prc,"fe%i"),m,e)))
  );
  return("0.0");
}


\\ ====================================================================
\\ Tabulating a real or complex function
\\ ====================================================================

TabFuncArr(f,x,d=0,s=" ",file="",head="") =
/* --------------------------------------------------------------------
 Tabulates the values of a function for an array of arguments.
 When the file argument is not empty, writes the output
 into a file (starting with an optional file header string).
 Otherwise, just dumps the output on the display.
 Arguments: 
 f specifies a function with a single argument of the same
      type as x and returning t_INT, t_REAL, or t_COMPLEX.
 x specifies an array (vector) of desired arguments
 d specifies the number of decimal digits to output
 s specifies a separator to be used between columns
 file is the file name string
 head is the header to dump before the actual data.

 When f returns t_REAL, two columns are output
    (argument value).
 When f returns t_INT, the output is converted to t_REAL
    (with rounding when truncated) 
 When f returns t_COMPLEX, the output has 3 columns
    (argument real_part imaginary_part) 

Example:
 x=vector(100,n,0.0+(n-1)*0.1);
 f=besselj(0,z);
 TabFuncArr(f,x,7,"\t","d:\\mydata\\test.csv","Table of J0");
 TabFuncArr(f,x,7);

Notice the escape sequences format for the tabulator and for
backslashes in the output file name
-------------------------------------------------------------------- */
{
  if(#x==0,error("FtabFuncZ: x is not an array!"));
  if(file!="",
   write(file,head);for(i=1,#x,
   write(file,ExpFormat(x[i],d,,,s),s,ExpFormat(f(x[i]),d,,,s))),
   print(head);for(i=1,#x,
   print(ExpFormat(x[i],d,,,s),s,ExpFormat(f(x[i]),d,,,s))));
}

TabFuncLinX(f,xini,xstep,xend,d=0,s=" ",file="",head="") =
/* --------------------------------------------------------------------
 Like TabFuncArr, but the array of arguments x is replaced by the
 definition of a linear 'grid' with an initial value xini, a step
 xstep, and a maximum value which may not be exceeded.
 xini,xstep,xend should be t_REAL or t_INT, but not t_COMPLEX. 

Example:
 x=vector(100,n,0.0+(n-1)*0.1);
 f=besselj(0,z);
 TabFuncLinX(f,0,0,1.9.9,7,"\t","c:\\mydata\\test.csv","Table of J0");
 TabFuncLinX(f,0,0,1.9.9,7);
-------------------------------------------------------------------- */
{
  if(#x==0,error("FtabFuncZ: x is not an array!"));
  my(x=xini+0.0);
  if(file!="",
   write(file,head);while(x<=xend,
   write(file,ExpFormat(x,d,,,s),s,ExpFormat(f(x),d,,,s));x+=xstep),
   print(head);while(x<=xend,
   print(ExpFormat(x,d,,,s),s,ExpFormat(f(x),d,,,s));x+=xstep));
}


\\ ====================================================================
\\ Preparing formatted OEIS entry items
\\ ====================================================================

Real2OEIS(file,cnst,dgts=1000) = 
/* --------------------------------------------------------------------
  Writes into a text file all one needs to submit to OEIS a
  real constant x, evaluated to current "realprecision"
  digits. The default precision of x is 105 digits.
  Arguments:
  file ... String specifying the file "path+name".
  cnst ... a t_REAL constant.
  dgts ... The number of desired digits (any natural number) 

  Appends the following text into the output "file.txt":
  <Input value>
  <OFFSET> \\ +1 for 3.1, 0 for 0.31, -5 for 3.1e-6
  <DATA>   
  [<105 digits, starting with the first non-zero one>]
  <B-File>   
  <offset+0 1st non-zero digit>
  <offset+1 2nd digit>
  <offset+2 2nd digit>
  <offset+3 2nd digit>
  etc., until
  <offset+dgts dgts-th digit>,
  or less, if cnst has a lower precision.


  User instructions:
  a) If possible, evaluate the argument cnst with at least
     110 digits. If you intend to upload a b-file with a higher
     precision than 105 digits (OEIS default for DATA section),
     evaluate cnst with that precision, plus some extra digits
     (better test how many are required by evaluating it twice
     with various precisions. 
     For example, if a precision of 2000 digits is desired,
     specify something like default(realprecision,2020) before
     evaluating cnst (including any preparatory values).
     Note that PARI includes a built-in estimate of how precise
     any specific t_REAL value is. If precision(cnst)-2 returns
     fewer digits than what you requested, the function will
     override your request (PARI is not infallible in its
     precision estimates and may be too optimistic).
     Even with all such precautions, the final decision about
     how many truly significant digits the result has is your
     responsability.
  b) Open the output file with Notepad or Wordpad.
     - First there is the plain decimal value of cnst (all
       digits). A reasonable section of that can be copy-pasted
       into the EXAMPLE section of the submission form.
     - Then there is a line with the OFFSET value. 
     - Then there is a block of max 105 digits, separated
       by comma+space, to copy and paste into "DATA" section.
     - Thereafter come two properly columns which constitute the
       B-File, to the specified precision (dgts), or less when
       cnst does not have the dgts precision.

  Once you have copied the DATA, OFFSET, and EXAMPLE sections,
  eliminate the file header until the "B-File:" line, and save
  what remains as the b-file. If you want to cut away some more
  trailing digits which you think might be in doubt, do so now,
  before uploading the b-file to OEIS. For example, having used
  2200 digits of realprecision, you might want to truncate the
  B-file to 2000 digits to be published.

  Example: Real2OEIS("c:\\myoeis\\b654321.txt",%33)
  Note the double backslashes in the file name string.
-------------------------------------------------------------------- */
{
  if (type(cnst)!="t_REAL",error("Argument must be t_REAL"));
  my (offst=0,digful=precision(cnst)-2,dig105=105,x,v);
  if (dig105>digful,dig105=digful);
  if (digful>dgts,digful=dgts);
  write(file,"Full input value:\n",cnst);
  if (cnst < 0.0, x = -cnst, x = cnst);
  while (x >= 1.0, x=x/10; offst=offst+1);
  while (x  < .10, x=10*x; offst=offst-1);
  write(file,"\nOffset = ",offst);
  v = digits(floor(10^dig105*x));
  write(file,"\nDATA:");
  for(i=1,#v-1,write1(file,v[i],", "));
  write1(file,v[#v]);
  write(file,"\n\nB-File:");
  v = digits(floor(x*10^digful));
  for(i=1,#v,write(file,offst+i-1," ",v[i]));
}


IntSeq2OEIS(file,vect,offset=1,dumpall=0) = 
/* --------------------------------------------------------------------
  Writes into a text file all one needs to submit to OEIS a
  precomputed vector of integers.

  Writes (appends!) the following text into the output file:
  <OFFSET:> \\ the value specified in the 'offset' argument
  <DATA:>
    <the entries of vect until exhausted, but not exceeding
     260 characters>
  <B-File:>   
    <offset   vect[1]>
    <offset+1 vect[2]>
    <offset+2 vect[3]>
    \\ etc., but see the next comment

  When the default (0) is used for dumpall, the output will
  terminate just before the first entry with >= 1000 digits 
  (if present). Otherwise, if dumpall is nonzero, all items
  are dumped, no matter how long.

  User instructions:
  a) Compute your integer sequence with as many elements as
     you can but not more than the maximum you want to appear
     in the b-file.
  b) Run the script, making sure to set the correct offset.
  c) Open the output file with Notepad or Wordpad.
     - First, there is a line reporting the OFFSET: value.
     - Then, there is a block introduced by a line labelled
       "DATA:" and containing of at most 260 characters,
       with entries separated by comma+space. These are
       to be copy/pasted into the OEIS DATA section.
     - Finally, there is a line labelled "B-File:", followed
       by two properly indexed columns which constitute the
       actual B-File. It extends until the whole vector is
       dumped, or until an entry has 1000 or more digits.

  To prepare the B-File, extract the section starting just
  after the line saying "B-File:" and extending up to the
  end of the file.
  
  Note: Should any of the 1st to 4th entries exceed 260
     characters, you have a special case. Less than 4 items
     will be dumped in DATA and you should better discuss
     the case with an OEIS editor.

  Example:
   v = vector(1000,n,(n-1)!);
   IntSeq2OEIS("c:\\myparidir\\b12345.txt",v,0)
   \\ The dump stops in this case at index 449 (450!>10^1000)
-------------------------------------------------------------------- */
{
  my (s=260);
  write(file,"OFFSET: ",offset,"\n\nDATA:");
  s -= sizedigit(vect[1]);
  for(i=1,#vect, if(s<=0,break);write1(file,vect[i]);
    if (i<#vect, s-=sizedigit(vect[i+1])+2;
        if (s>0,write1(file,", ")));
  );
  write(file,"\n\nB-File:");
  for(i=1,#vect,
    if((dumpall==0)&&(sizedigit(vect[i])>=1000),break); 
    write(file,offset+i-1," ",vect[i]);
  );
}


/*
=======================================================================
 Contributed to OEIS Wiki by Stanislav Sykora
=======================================================================
*/
